package cn.zaichi.edu.excelcommon.utils.impl;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import cn.zaichi.edu.excelcommon.annotations.ExcelDynamicTitle;
import cn.zaichi.edu.excelcommon.annotations.ExcelDynamicValue;
import cn.zaichi.edu.excelcommon.annotations.ExcelField;
import cn.zaichi.edu.excelcommon.constants.ImportResultState;
import cn.zaichi.edu.excelcommon.entity.BaseImportParam;
import cn.zaichi.edu.excelcommon.entity.DynamicEntity;
import cn.zaichi.edu.excelcommon.entity.ErrorCellEntity;
import cn.zaichi.edu.excelcommon.entity.ImportResult;
import cn.zaichi.edu.excelcommon.entity.ImportTitleEntity;
import cn.zaichi.edu.excelcommon.exception.ExcelException;
import cn.zaichi.edu.excelcommon.handler.BaseExcelHandler;
import cn.zaichi.edu.excelcommon.plugs.Validator;
import cn.zaichi.edu.excelcommon.plugs.convertor.VoidConvertor;
import cn.zaichi.edu.excelcommon.process.ExcelImportDataInfo;
import cn.zaichi.edu.excelcommon.process.ProcessingExcel;
import cn.zaichi.edu.excelcommon.utils.ConvertUtil;
import cn.zaichi.edu.excelcommon.utils.DateFormatUtil;
import cn.zaichi.edu.excelcommon.utils.ExcelImport;
import cn.zaichi.edu.excelcommon.utils.ImportCellValue;
import cn.zaichi.edu.excelcommon.utils.ImportTitleUtil;
import cn.zaichi.edu.excelcommon.utils.ImportWorkbookUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 使用 bean 方式导入excel 工具类
 */
public class ExcelBeanImportUtil<T> implements ExcelImport {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    //合并单元格
    List<CellRangeAddress> combineCell;
    Workbook workbook;
    BaseExcelHandler excelHandler;
    //动态列
    DynamicEntity dynamicEntity;
    //表头
    List<ImportTitleEntity> titleEntityList;
    //最终极表头
    List<ImportTitleEntity> lastTitleList;
    //导入参数
    BaseImportParam param;

    //返回结果
    ImportResult result;

    @Override
    public ImportResult getResult() {
        return result;
    }

    public ExcelBeanImportUtil(BaseImportParam param) throws Exception {
        try {
            this.param=param;
            //获取工作表格
            workbook = ImportWorkbookUtil.onStart(param.getFilePath(),param.getFilePass());
            //动态列
            dynamicEntity=ImportTitleUtil.getDynamic(workbook,param.getClz(),param.getTitleEndRow(),param.getStartColumn());
            //第一个sheet的合并单元格
            combineCell=ImportCellValue.getCombineCell(workbook.getSheetAt(0));
            //表头
            titleEntityList=ImportTitleUtil.readExcelTitle(workbook,combineCell,param.getTitleStarRow(),param.getTitleEndRow(),param.getStartColumn());
            lastTitleList=ImportTitleUtil.getLastTitle(titleEntityList);
            //返回树形表头
            List<ImportTitleEntity> titleTree = ImportTitleUtil.getTitleTree(titleEntityList);
            //返回结果
            result=new ImportResult(titleTree,new HashMap<>(),new ArrayList<>());


        } catch (IOException e) {
           throw e;
        }
    }




    /**
     * 导入操作
     */
    public void importExcel(ProcessingExcel processing) throws InstantiationException, IllegalAccessException {
        Field[] fields = this.param.getClz().getDeclaredFields();
        ExcelImportDataInfo importDataInfo=new ExcelImportDataInfo();
        importDataInfo.setSheetCount(workbook.getNumberOfSheets());
        importDataInfo.setUuid(param.getUuid());
        for (int numSheet = 0; numSheet < workbook.getNumberOfSheets(); numSheet++) {
            List<T> dataMapList=new ArrayList<>();

            Sheet sheet = workbook.getSheetAt(numSheet);

            excelHandler=ImportWorkbookUtil.getExcelHandler(excelHandler,importDataInfo,sheet,param);
            for (int rowNum = param.getStartRow(); rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row hssfRow = sheet.getRow(rowNum);
                if (hssfRow == null) {
                    continue;
                }
                T t = setFieldValue(fields, numSheet, sheet, rowNum, processing);
                excelHandler.onFinishRow(numSheet,rowNum-param.getStartRow()+1,processing);
                dataMapList.add(t);
            }
            excelHandler.onFinishSheet(numSheet,processing);
            //设置返回结果
            result.getDataMap().put(sheet.getSheetName(),dataMapList);
        }
        excelHandler.onFinishExcel(processing);
    }

    /**
     * 设置字段值
     * @param sheetNum
     * @param sheet
     * @param rowNum
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private T setFieldValue(Field[] fields,int sheetNum,Sheet sheet,int rowNum,ProcessingExcel processing) throws IllegalAccessException, InstantiationException {
        T entity= (T) param.getClz().newInstance();
        List<ErrorCellEntity> cellEntityList=new ArrayList<>();
        for (Field field : fields) {
            field.setAccessible(true);
            ExcelField fieldAnnotation = field.getAnnotation(ExcelField.class);
            if(fieldAnnotation==null){
                continue;
            }
            if (fieldAnnotation.sort() > -1) {
                setValue(entity,field,sheetNum,sheet,rowNum,fieldAnnotation,cellEntityList,processing);

            }else if (fieldAnnotation.dynamic() && StringUtils.isNotBlank(fieldAnnotation.splitFlage())&&field.getType() == List.class) {
                /**动态列读取*/
                getDynamicList(entity,field, fieldAnnotation, sheetNum, rowNum, sheet,cellEntityList, processing);
            }
        }

        if(cellEntityList.stream().anyMatch(e->e.getState()==500)){
            result.getErrorCellEntitieList().add(cellEntityList);
        }
        return entity;
    }

    /**
     * 字段赋值
     * @param entity
     * @param field
     * @param sheetNum
     * @param sheet
     * @param rowNum
     * @param fieldAnnotation
     * @param cellEntityList 错误数据存放
     * @param processing
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private void setValue(T entity,Field field,int sheetNum,Sheet sheet,int rowNum,ExcelField fieldAnnotation,List<ErrorCellEntity> cellEntityList,ProcessingExcel processing){
        //对应表头
        ImportTitleEntity titleEntity = lastTitleList.stream().filter(title -> title.getCode().split("_")[1].equals((fieldAnnotation.sort()+param.getStartColumn()) + "")).findFirst().orElse(null);
        String titleCode=titleEntity.getCode();
        try {
            String type = field.getGenericType().getTypeName();
            String cellValue = ImportCellValue.getCellValue(combineCell, sheet, rowNum, fieldAnnotation.sort()+param.getStartColumn());
            //字段赋值
            Object value = getFeildValue(type, cellValue, fieldAnnotation);
            field.set(entity, value);

            /**处理excel日期 格式*/
            if (type.equals("java.lang.String")) {
                if(StringUtils.isNotEmpty(fieldAnnotation.dateFormat())){
                    cellValue = DateFormatUtil.LocalDateToString(cellValue,fieldAnnotation.dateFormat());
                }
            }
            cellEntityList.add(new ErrorCellEntity(sheetNum,rowNum,titleCode,cellValue,200));
            excelHandler.onSuccessCell(sheetNum,rowNum,0,cellValue,processing);
        }catch (ExcelException ex){
            cellEntityList.add(new ErrorCellEntity(sheetNum,rowNum,titleCode,ex.getMsg(),500));
            excelHandler.onErrorCell(sheetNum,rowNum,0,ex.getMsg(),ex.getStatus(),processing);
            result.setState(ImportResultState.onPartError);
        }
        catch (Exception ex){
            logger.error(ex.getMessage());
            cellEntityList.add(new ErrorCellEntity(sheetNum,rowNum,titleCode,"未知错误",500));
            //错误信息
            excelHandler.onErrorCell(sheetNum,rowNum,0,"未知错误",500,processing);
            result.setState(ImportResultState.onPartError);

        }

    }

    /**
     * 动态列赋值
     * @param field
     * @param fieldAnnotation
     * @param sheetNum
     * @param rowNum
     * @param sheet
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private void getDynamicList(T entity,Field field,ExcelField fieldAnnotation,int sheetNum,int rowNum,Sheet sheet, List<ErrorCellEntity> cellEntityList,ProcessingExcel processing) throws IllegalAccessException, InstantiationException {
        List list = new ArrayList();
        Type genericType = field.getGenericType();
        if (genericType == null) {
            return;
        }
        if (genericType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) genericType;
            Class<?> genericClz = (Class<?>) pt.getActualTypeArguments()[0];
            if (!genericClz.getPackage().getName().startsWith("cn.zaichi.edu.")) {
                return;
            }
            Field[] genericClzFields = genericClz.getDeclaredFields();
            List<String> dynamicNameList = dynamicEntity.getDynamicNames().get(fieldAnnotation.splitFlage());
            List<Integer> dynamicCellIndex = dynamicEntity.getDynamicColunmns().get(fieldAnnotation.splitFlage());
            int i = 0;
            for (String dynamicName : dynamicNameList) {
                Object o = genericClz.newInstance();
                for (Field gClzField : genericClzFields) {
                    gClzField.setAccessible(true);
                    ExcelDynamicTitle dynamicTitle = gClzField.getAnnotation(ExcelDynamicTitle.class);
                    if (dynamicTitle != null) {
                        gClzField.set(o, dynamicName);
                    }
                    ExcelDynamicValue dynamicValue = gClzField.getAnnotation(ExcelDynamicValue.class);
                    if (dynamicValue != null) {
                        //对应表头
                        int j=i;
                        ImportTitleEntity titleEntity = lastTitleList.stream().filter(title -> title.getCode().split("_")[1].equals(dynamicCellIndex.get(j) + "")).findFirst().orElse(null);
                        String titleCode=titleEntity.getCode();
                        try {
                            String type = gClzField.getGenericType().getTypeName();
                            String cellValue=ImportCellValue.getCellValue(combineCell, sheet, rowNum, dynamicCellIndex.get(i));
                            Object feildValue = getFeildValue(type, cellValue, fieldAnnotation);
                            //设置字段值
                            gClzField.set(o, feildValue);
                            i++;
                            list.add(o);
                            cellEntityList.add(new ErrorCellEntity(sheetNum,rowNum,titleCode,cellValue,200));
                            excelHandler.onSuccessCell(sheetNum,rowNum,0,cellValue,processing);
                        }catch (ExcelException ex){
                            cellEntityList.add(new ErrorCellEntity(sheetNum,rowNum,titleCode,ex.getMsg(),500));
                            excelHandler.onErrorCell(sheetNum,rowNum,0,ex.getMsg(),ex.getStatus(),processing);
                            result.setState(ImportResultState.onPartError);
                        }
                        catch (Exception ex){
                            cellEntityList.add(new ErrorCellEntity(sheetNum,rowNum,titleCode,"未知错误",500));
                            //错误信息
                            excelHandler.onErrorCell(sheetNum,rowNum,0,"未知错误",500,processing);
                            result.setState(ImportResultState.onPartError);
                        }

                    }

                }
            }


        }
        field.set(entity, list);
    }

    /**
     * 转换字段属性值
     * @param fiedType 字段类型
     * @param cellValue 单元格内容
     * @param fieldAnnotation 字段注解
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private Object getFeildValue(String fiedType,String cellValue,ExcelField fieldAnnotation) throws IllegalAccessException, InstantiationException {
        //字段赋值
        Object value;
        //字段转换
        if(fieldAnnotation.rConvert()!=VoidConvertor.class){
            value=fieldAnnotation.rConvert().newInstance().readerConvert(cellValue,fieldAnnotation.rParam());
        }else{
            value=ConvertUtil.getValue(fiedType,cellValue,fieldAnnotation.dateFormat());
        }
        //字段验证
        if(fieldAnnotation.validator()!=null&&fieldAnnotation.validator().length>0) {
            for (Class<? extends Validator> validator : fieldAnnotation.validator()) {
                validator.newInstance().valid(value);
            }
        }
        return value;
    }
}
